/**
 * Copyright (C) 2009 Matteo Merli <mm@merlimat.org>
 * 
 * $Id: FunctionCall.java 17 2009-01-12 12:36:59Z matteo.merli $
 * $URL: http://pxl-lang.googlecode.com/svn/trunk/src/main/java/pxl/types/util/FunctionCall.java $
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package pxl.types.util;

import java.util.List;

import pxl.errors.TypeError;
import pxl.types.PxlBoolean;
import pxl.types.PxlIterator;
import pxl.types.PxlMap;
import pxl.types.PxlObject;
import pxl.types.PxlString;
import pxl.types.PxlTuple;

import com.google.common.collect.Lists;

public class FunctionCall
{

    public static Object[] getArgumentValues( String functionName,
            PxlParametersList parameters, List<PxlArgument> args )
    {

        List<Object> values = Lists.newArrayListWithExpectedSize( parameters
                .positionalParameters() );
        for ( int i = 0; i < parameters.positionalParameters(); i++ )
            values.add( null );

        List<PxlObject> tupleValues = null;
        if ( parameters.hasTupleParam() )
            tupleValues = Lists.newArrayList();

        // First check positional parameters
        int a = 0;
        int p = 0;
        while ( a < args.size() )
        {
            PxlArgument pxlArgument = args.get( a );
            if ( pxlArgument.name() != null || pxlArgument.isMap() )
                // It wasn't a positional parameter
                break;

            if ( pxlArgument.isTuple() )
            {
                // Use the sequence as positional parameters
                int i = a;
                PxlIterator it = pxlArgument.value().__getiterator__();
                args.remove( a );
                while ( it.hasNext() == PxlBoolean.True )
                {
                    PxlObject value = it.next();
                    args.add( i++, new PxlArgument( value ) );
                }

                continue;
            }

            if ( p >= parameters.positionalParameters() )
            {
                if ( parameters.hasTupleParam() )
                {
                    tupleValues.add( pxlArgument.value() );
                }
                else
                {
                    String msg = String.format( "%s() takes %s %d "
                            + "positional argument%s (%d given)", functionName,
                            parameters.hasExactNumberOfParameters() ? "exactly"
                                    : "at most", parameters
                                    .positionalParameters(), parameters
                                    .positionalParameters() > 1 ? "s" : "",
                            args.size() );
                    throw new TypeError( msg );
                }
            }
            else
                values.set( a, pxlArgument.value() );

            ++a;
            ++p;
        }
        /*
         * if ( args.size() < parameters.mandatoryParameters() ) { String msg =
         * String.format( "%s() takes %s %d " +
         * "positional argument%s (%d given)", functionName,
         * parameters.hasExactNumberOfParameters() ? "exactly" : "at most",
         * parameters.positionalParameters(), parameters.positionalParameters()
         * > 1 ? "s" : "", args .size() ); throw new TypeError( msg ); }
         */

        // Keyword arguments (name=value)
        PxlMap mapValues = null;
        if ( parameters.hasMapParam() )
            mapValues = new PxlMap();

        while ( a < args.size() )
        {
            PxlArgument pxlArgument = args.get( a );

            if ( pxlArgument.isMap() )
            {
                // Use the map as keyword parameters
                if ( !(pxlArgument.value() instanceof PxlMap) )
                    throw new TypeError( "Expecting a map object" );

                PxlMap map = (PxlMap) pxlArgument.value();
                int i = a;
                PxlIterator it = map.__getiterator__();
                args.remove( a );
                while ( it.hasNext() == PxlBoolean.True )
                {
                    PxlObject key = it.next();
                    if ( !(key instanceof PxlString) )
                        throw new TypeError( "Keys must be strings." );
                    args.add( i++, new PxlArgument( key.toString(), map
                            .__getitem__( key ) ) );
                }

                continue;
            }

            // Find position
            boolean found = false;
            int i;
            for ( i = 0; i < parameters.positionalParameters(); i++ )
                if ( pxlArgument.name().equals( parameters.get( i ).name() ) )
                {
                    found = true;
                    break;
                }

            if ( !found )
            {
                if ( parameters.hasMapParam() )
                {
                    mapValues.put( new PxlString( pxlArgument.name() ),
                            pxlArgument.value() );
                }
                else
                {
                    String msg = String.format(
                            "%s() got an unexpected keyword argument '%s'",
                            functionName, pxlArgument.name() );
                    throw new TypeError( msg );
                }
            }
            else if ( values.get( i ) != null )
            {
                String msg = String.format(
                        "%s() got multiple values for keyword argument '%s'",
                        functionName, pxlArgument.name() );
                throw new TypeError( msg );
            }
            else
                values.set( i, pxlArgument.value() );

            ++a;
        }

        if ( tupleValues != null )
            values.add( new PxlTuple( tupleValues ) );

        if ( mapValues != null )
            values.add( mapValues );

        return values.toArray();
    }
}
